
// ===============================================================================================================
// -*- C++ -*-
//
// Camera.hpp - A first person style 3D camera.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#ifndef __CAMERA_HPP__
#define __CAMERA_HPP__

// Local includes
#include <MathLib.hpp>
using namespace MathLib;

// =========================================================
// Camera -- A "Quake like" first person 3D camera.
// =========================================================

class Camera {

public:

	// Public Interface:

	Camera(void);

	Camera(const Vec3f & rightVec, const Vec3f & upVec, const Vec3f & forwardVec, const Vec3f & eyeVec);

	// Resets to a "starting" position.
	void Reset(const Vec3f & rightVec, const Vec3f & upVec, const Vec3f & forwardVec, const Vec3f & eyeVec);

	// Pitches camera by "angle" (in radians).
	void Pitch(float angle);

	// Rotates around world Y-axis by the given angle (in radians).
	void Rotate(float angle);

	// Valid directions to move the camera in:
	enum MoveDir {

		FORWARD, // Move forward relative to the camera's space
		BACK,    // Move backward relative to the camera's space
		LEFT,    // Move left relative to the camera's space
		RIGHT    // Move right relative to the camera's space
	};

	// Moves the camera by the given direction, using the provided movement amount.
	// The last three parameters indicate in which axis to move. If it is equal to 1, move in that axis, if it is zero don't move.
	void Move(MoveDir dir, float amount, float x, float y, float z);

	// Get / Set:

	inline void SetRight(const Vec3f & rightVec) { right = rightVec; }
	inline void SetUp(const Vec3f & upVec) { up = upVec; }
	inline void SetForward(const Vec3f & forwardVec) { forward = forwardVec; }
	inline void SetEye(const Vec3f & eyeVec) { eye = eyeVec; }

	inline Vec3f GetRight(void) const { return (right); }
	inline Vec3f GetUp(void) const { return (up); }
	inline Vec3f GetForward(void) const { return (forward); }
	inline Vec3f GetEye(void) const { return (eye); }

	// This function returns what the camera is looking at. Our eye is ALWAYS the origin
	// of camera's coordinate system and we are ALWAYS looking straight down the "forward" axis
	// so to calculate the target it's just a matter of adding the eye plus the forward.
	inline Vec3f GetTarget(void) const
	{
		return (Vec3f(eye.x + forward.x, eye.y + forward.y, eye.z + forward.z));
	}

private:

	/*	Initial Camera Axes:

		(up)
		+Y   +Z (forward)
		|   /
		|  /
		| /
		+ ------ +X (right)
	*/

	// This allows us to rotate "vec" around an arbitrary "axis" by a the "angle" in radians.
	static void RotateAroundAxis(Vec3f & result, const Vec3f & vec, const Vec3f & axis, float angle);

private:

	// Private Data:

	Vec3f right;   // The normalized axis that points to the "right"
	Vec3f up;      // The normalized axis that points "up"
	Vec3f forward; // The normalized axis that points "forward"
	Vec3f eye;     // The position of the camera (i.e. the camera's eye and the origin of the camera's coordinate system)
};

#endif // __CAMERA_HPP__